package check

import (
	"gitee.com/u-language/u-language/ucom/ast"
	"gitee.com/u-language/u-language/ucom/astdata"
	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/internal/utils"
)

// checkCodeBlock 检查一个代码块
//   - Left是代码块第一个节点在抽象语法树节点切片的偏移量
//   - Right是代码块右大括号节点在抽象语法树节点切片的偏移量
//   - sbt是代码块所属的符号表
//   - tree是检查的抽象语法树，不能为nil
func checkCodeBlock(Left, Right int, sbt *ast.Sbt, table symbolCheckTable, ltabel labelTable, tree *ast.Tree, funcinfo *ast.FuncInfo, isinfor bool, goto_info []gotoStmtInfo, isswitchin bool, switch_expr_typ string, InAutoFree bool, IsGeneric bool, Nodes []ast.Node, off int) []codeBlockErr {
	var ret = make([]codeBlockErr, 0)
	var code errcode.ErrCode
	var msg errcode.Msg
	var left, right int
	isfunc := false
	foff := 0
	ismethod := false
	if Left != 0 {
		_, ismethod = Nodes[Left-1].(*ast.MethodNode)
		_, isfunc = Nodes[Left-1].(*ast.FuncNode)
		foff = Left - 1
	} else if Nodes[0] != nil {
		_, ismethod = Nodes[0].(*ast.MethodNode)
		_, isfunc = Nodes[0].(*ast.FuncNode)
	}
	if isfunc { //如果是函数代码块
		info := sbt.Have(Nodes[foff].(*ast.FuncNode).Name)
		funcinfo = info.PtrFuncInfo()
		goto_info = make([]gotoStmtInfo, 0)
		table.addParame(info.PtrFuncInfo().Parame, Left+1) //将函数参数视为局部变量
		if len(funcinfo.TypeParame) != 0 {                 //如果是未实例化泛型，清除泛型节点
			var left, right int
			if IsGeneric {
				left, right = Left+1, (Right - Left + 1)
			} else {
				left, right = Left, Right
			}
			clearGeneric(Nodes, left-1, right+1) //减1为了连声明一起清除，加1是为了连右大括号一起清除
			return nil
		}
	}
	if ismethod {
		n := Nodes[foff].(*ast.MethodNode)
		funcinfo = n.FuncInfo
		table.addParame(funcinfo.Parame, Left+1)
		if len(funcinfo.TypeParame) != 0 { //如果是未实例化泛型，清除泛型节点
			var left, right int
			if IsGeneric {
				left, right = Left+1, (Right - Left + 1)
			} else {
				left, right = Left, Right
			}
			clearGeneric(Nodes, left-1, right+1) //减1为了连声明一起清除，加1是为了连右大括号一起清除
			return nil
		}
	}
	for i := Left; i < Right; i++ {
		if utils.IsNil(Nodes[i]) {
			continue
		}
		switch n := Nodes[i].(type) {
		case *ast.VarNode: //是变量声明节点
			table.Add(n.Name, i+1) //记录声明行数
			code, msg = checkVar(n, sbt, tree, InAutoFree)
			if code != errcode.NoErr { //如果使用在声明之前
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
			}
		case *ast.ConstNode: //是常量节点
			table.Add(n.Name, i+1) //记录声明行数
			code, msg = checkConst(n, sbt, tree, InAutoFree)
			if code != errcode.NoErr { //如果使用在声明之前
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
			}
			if n.Value != nil {
				code, msg := checkSrcUseSymbol(n.Value, table, sbt)
				if code != errcode.NoErr { //如果使用在声明之前
					ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
				}
			}
		case *ast.ASSIGNNode:
			code, msg = CheckASSIGN(n, sbt, tree, InAutoFree)
			if code != errcode.NoErr {
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
				continue
			}
			code, msg, info, is_local_decl, _ := ret_info(n.Dest, sbt, tree, InAutoFree)
			if code != errcode.NoErr {
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
				continue
			}
			if is_local_decl { //目的操作数变量在当前作用域声明
				code := table.Check(info.Name()) //检查使用是否在声明之前
				if code != errcode.NoErr {       //如果使用在声明之前
					ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: errcode.NewMsgSymbol(info.Name())})
				}
			}
			code, msg = checkSrcUseSymbol(n.Src, table, sbt) //检查源操作数
			if code != errcode.NoErr {                       //如果使用在声明之前
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
			}
		case *ast.IfNode: //是if节点
			code, msg = CheckBoolExprNode(n.BoolExpr, n.Sbt, tree, InAutoFree)
			if code != errcode.NoErr {
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
			}
			if IsGeneric {
				left, right = i+1, (n.Right-n.Left+1)+i
			} else {
				left, right = n.Left, n.Right
			}
			ret = append(ret, checkCodeBlock(left, right, n.Sbt, table, ltabel, tree, funcinfo, isinfor, goto_info, isswitchin, switch_expr_typ, InAutoFree, IsGeneric, Nodes, off)...)
			i = right
		case *ast.ElseNode: //是else节点
			code, msg = CheckBoolExprNode(n.BoolExpr, n.Sbt, tree, InAutoFree)
			if code != errcode.NoErr {
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
			}
			if IsGeneric {
				left, right = i+1, (n.Right-n.Left+1)+i
			} else {
				left, right = n.Left, n.Right
			}
			ret = append(ret, checkCodeBlock(left, right, n.Sbt, table, ltabel, tree, funcinfo, isinfor, goto_info, isswitchin, switch_expr_typ, InAutoFree, IsGeneric, Nodes, off)...)
			i = right
		case *ast.ForNode: //是for节点
			if n.InitStmt != nil {
				vn, ok := n.InitStmt.(*ast.VarNode)
				if ok {
					table.Add(vn.Name, i+1)
				}
			}
			code, msg = checkFor(n, tree, InAutoFree)
			if code != errcode.NoErr {
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
			}
			if IsGeneric {
				left, right = i+1, (n.Right-n.Left+1)+i
			} else {
				left, right = n.Left, n.Right
			}
			ret = append(ret, checkCodeBlock(left, right, n.Sbt, table, ltabel, tree, funcinfo, true, goto_info, isswitchin, switch_expr_typ, InAutoFree, IsGeneric, Nodes, off)...)
			i = right
		case *ast.ReturnNode: //是返回语句
			code, msg, typestr := ret_type_str(n.RetValue, sbt, tree, InAutoFree)
			if code != errcode.NoErr {
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
				continue
			}
			if len(funcinfo.RetValue) != 0 {
				if !utils.Typ_is_equal(typestr, funcinfo.RetValue[0].Type.Typ()) { //如果声明的类型与返回的类型不相等
					ret = append(ret, codeBlockErr{Line: off + i + 1, Code: errcode.ReturnErr, Msg: errcode.NewMsgReturnTypeIsNotEqual(funcinfo.RetValue[0].Type.Typ(), typestr)})
					continue
				}
			}
		case *ast.LabelNode: //是标签
			ltabel.Add(n.Value)
		case ast.BreakStmt: //是berak语句
			if !isinfor && !isswitchin { //如果同时不在for代码块和switch代码块中
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: errcode.BreakStmtInForOrSwitch, Msg: nil})
			}
		case ast.ContinueStmt: //是continue语句
			if !isinfor { //如果不在for代码块中
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: errcode.ContinueStmtInFor, Msg: nil})
			}
		case *ast.SelfOpStmt: //是自操作语句
			code, msg = checkSelfOpStmtCtx(n, sbt, tree, InAutoFree)
			if code != errcode.NoErr {
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
			}
		case *ast.CallNode: //是调用节点
			code, msg, _ = checkCall(n, sbt, tree, InAutoFree)
			if code != errcode.NoErr {
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
			}
		case *ast.GotoStmt: //是goto语句
			goto_info = append(goto_info, gotoStmtInfo{Ptr: n, LineNum: off + i + 1})
		case *ast.FuncNode: //是函数声明
			if IsGeneric {
				left, right = i+1, (n.Right-n.Left+1)+i
			} else {
				left, right = n.Left, n.Right
			}
			ret = append(ret, checkCodeBlock(left, right, n.Sbt, table, ltabel, tree, funcinfo, isinfor, goto_info, isswitchin, switch_expr_typ, InAutoFree, IsGeneric, Nodes, off)...)
			i = right
		case *ast.SwitchNode: //是switch语句
			code, msg := checkSrcUseSymbol(n.Expr, table, sbt)
			if code != errcode.NoErr { //如果使用在声明之前
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
			}
			code, msg, switch_expr_typ = ret_type_str(n.Expr, n.Sbt, tree, InAutoFree)
			if code != errcode.NoErr { //如果有错误
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
				break
			}
			var left, right int
			if IsGeneric {
				left, right = i+1, (n.Right-n.Left+1)+i
			} else {
				left, right = n.Left, n.Right
			}
			ret = append(ret, checkCodeBlock(left, right, n.Sbt, table, ltabel, tree, funcinfo, isinfor, goto_info, true, switch_expr_typ, InAutoFree, IsGeneric, Nodes, off)...)
			i = right
		case *ast.CaseNode: //是case语句
			if !isswitchin { //如果不在switch中
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: errcode.CaseStmtInSwitch, Msg: nil})
			}
			code, msg := checkSrcUseSymbol(n.Expr, table, sbt)
			if code != errcode.NoErr { //如果使用在声明之前
				ret = append(ret, codeBlockErr{Line: i + 1, Code: code, Msg: msg})
			}
			_, _, typ := ret_type_str(n.Expr, n.Sbt, tree, InAutoFree)
			if typ != switch_expr_typ && switch_expr_typ != "" {
				ret = append(ret, codeBlockErr{Line: i + 1, Code: errcode.TypeIsNotEqual, Msg: errcode.NewMsgSwitchAndCaseTypNoEqual(switch_expr_typ, typ)})
			}
		case *ast.MethodNode: //是方法声明
			if IsGeneric {
				left, right = i+1, (n.Right-n.Left+1)+i
			} else {
				left, right = n.Left, n.Right
			}
			ret = append(ret, checkCodeBlock(left, right, n.Sbt, table, ltabel, tree, funcinfo, isinfor, goto_info, isswitchin, switch_expr_typ, true, IsGeneric, Nodes, off)...)
			i = right
		case *ast.AutoFreeNode: //是自动释放块
			code, msg, expr_typ := ret_type_str(n.Expr, n.Sbt, tree, InAutoFree)
			if code != errcode.NoErr { //如果有错误
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: code, Msg: msg})
				break
			}
			if expr_typ != enum.Int { //如果表达式不表示整数
				ret = append(ret, codeBlockErr{Line: off + i + 1, Code: errcode.ExprForAutoFreeShouldExprInt, Msg: nil})
				break
			}
			var left, right int
			if IsGeneric {
				left, right = i+1, (n.Right-n.Left+1)+i
			} else {
				left, right = n.Left, n.Right
			}
			ret = append(ret, checkCodeBlock(left, right, n.Sbt, table, ltabel, tree, funcinfo, isinfor, goto_info, isswitchin, switch_expr_typ, true, IsGeneric, Nodes, off)...)
			i = right
		}
	}
	if isfunc || ismethod { //如果是函数，检查goto语句
		ret = append(ret, checkAllgoto(goto_info, ltabel)...)
	}
	return ret
}

func clearGeneric(nodes []ast.Node, Left int, Right int) {
	for i := Left; i < Right; i++ {
		nodes[i] = nil
	}
}

// checkSrcUseSymbol 检查节点中的变量，使用是否在声明之前
func checkSrcUseSymbol(src ast.Node, table symbolCheckTable, sbt *ast.Sbt) (errcode.ErrCode, errcode.Msg) {
	switch src := src.(type) {
	case *ast.Object: //是对象节点
		if src.Kind == ast.SymbolObj {
			if _, ok := sbt.Have2(src.Name); ok { //使用的变量是在当前作用域声明
				if code := table.Check(src.Name); code != errcode.NoErr { //如果变量使用在声明之前
					return code, errcode.NewMsgSymbol(src.Name)
				}
			}
		}
	case *ast.OpExpr: //是运算表达式节点
		code, msg := checkSrcUseSymbol(src.Src1, table, sbt) //检查源操作数1
		if code != errcode.NoErr {                           //发现错误
			return code, msg
		}
		code, msg = checkSrcUseSymbol(src.Src2, table, sbt) //检查源操作数2
		if code != errcode.NoErr {                          //发现错误
			return code, msg
		}
	}
	return errcode.NoErr, nil
}

type symbolCheckTable map[string]int //key=符号名 value=是声明的行数

// Add 记录变量声明的行数，ast保证同一作用域不会有重名变量
func (t symbolCheckTable) Add(name string, line int) {
	t[name] = line
}

// Check 检查使用是否在声明之前
func (t symbolCheckTable) Check(name string) errcode.ErrCode {
	_, ok := t[name]
	if !ok {
		return errcode.SymbolUseBeforeDeclaration
	}
	return errcode.NoErr
}

// addParame 添加参数为已声明的局部变量
func (t symbolCheckTable) addParame(parame []astdata.Parame, line int) {
	for _, v := range parame {
		t.Add(v.Name, line)
	}
}

type codeBlockErr struct {
	Msg  errcode.Msg
	Line int
	Code errcode.ErrCode
}

// labelTable 标签表
type labelTable map[string]struct{}

// Add 记录变量声明的行数，ast保证同一作用域不会有重名变量
func (t labelTable) Add(name string) {
	t[name] = struct{}{}
}

// Have 检查使用是否在声明之前
func (t labelTable) Have(name string) bool {
	_, ok := t[name]
	return ok
}
